#ifndef _POINTER_HPP_
#define _POINTER_HPP_

typedef void(*free_handler)(void *);
struct mem {
    unsigned int ref;
    void * ptr;

    mem(unsigned int _ref = 0, void * _ptr = nullptr):
        ref(_ref), ptr(_ptr) {}
};

template <typename T, free_handler delete_handler = nullptr>
class pointer {
public:
    pointer(): ptr(nullptr) {}

    explicit pointer(T * entry) {
        ptr = new mem(1, entry);
    }

    pointer(pointer<T> const &rhs) {
        ptr = rhs.ptr;
        if(ptr) {
            ptr->ref++;
        }
    }

    pointer(pointer<T> && rhs) noexcept {
        ptr = rhs.ptr;
        rhs.ptr = nullptr;
    }

    ~pointer() {
        ref_minus();
    }

public:
    explicit operator bool() const {
        if(ptr) return true;
        return false;
    }

    unsigned int count() const {
        if(ptr) {
            return ptr->ref;
        }
        return 0;
    }

    /**
     * @brief Get the pointer object
     * 谨慎使用该函数
     * 
     * @return T* 
     */
    T* get_pointer() const {
        if(ptr) {
            return (T*)(ptr->ptr);
        }
        return nullptr;
    }

    T& operator*() const {
        return *(T *)(ptr->ptr);
    }

    T* operator->() const {
        return &(this->operator*());
    }

    pointer<T>& operator=(pointer<T> && rhs) noexcept {
        if(this == &rhs) {
            return *this;
        }

        ref_minus();

        ptr = rhs.ptr;
        return *this;
    }

    pointer<T>& operator=(pointer<T> const &rhs) {
        if(this == &rhs) {
            return *this;
        }

        ref_minus();

        rhs.ref_plus();
        ptr = rhs.ptr;
        return *this;
    }

private:
    /**
     * @brief 引用次数加一 
     * 
     */
    void ref_plus() const {
        if(!ptr) return;

        ptr->ref++;
    }

    /**
     * @brief 引用次数减一 
     * 
     */
    void ref_minus() {
        if(!ptr) return;

        ptr->ref--;
        if(!ptr->ref) {
            if(delete_handler) {
                delete_handler(ptr->ptr);
            }
            else {
                delete (T*)(ptr->ptr);
            }
            delete ptr;
            ptr = nullptr;
        }
    }
    
private:
    mem * ptr;
};

#endif